using System;
using System.Collections.Generic;
using System.Linq;
using Arcane.Source.Client.Extensions;
using Arcane.Source.Combat;
using Arcane.Source.Combat.StatusEffects;
using Godot;

namespace Arcane.Source.Data
{
    public class StatusEffectDB
    {
        public delegate BaseStatusEffect CreateFunc(Variant[] args);

        public readonly Dictionary<StringName, StatusEffectProto> statusEffects = [];
        public readonly Dictionary<StringName, CreateFunc> createFuncs = [];

        public void Init(TextureDB textures)
        {
            InitCreateFuncs();

            Register(
                "burning",
                textures.Get("spell/ignite")
            );

            Register(
                "mark",
                textures.Get("spell/mark")
            );

            Register(
                "over_charge",
                textures.Get("spell/over_charge")
            );

            Register(
                "shield",
                textures.Get("spell/shield")
            );
        }

        private void InitCreateFuncs()
        {
            RegisterCreateFunc("burning.new", CtorOf<Burning>());
            RegisterCreateFunc("mark.new", CtorOf<Mark>());
            RegisterCreateFunc("over_charge.new", CtorOf<OverCharge>());
            RegisterCreateFunc("shield.new", CtorOf<Shield>());
        }

        private void Register(StringName id, Texture2D icon)
        {
            statusEffects.Add(id, new StatusEffectProto(id, icon, id + ".new"));
        }

        private void RegisterCreateFunc(StringName id, CreateFunc func)
        {
            createFuncs.Add(id, func);
        }

        public StatusEffectProto Get(StringName effectId)
        {
            return statusEffects.TryGetValue(effectId, out var proto) ? proto : null;
        }

        public BaseStatusEffect Create(StringName effectId, params Variant[] args)
        {
            if (statusEffects.TryGetValue(effectId, out var proto))
            {
                return Create(proto, args);
            }
#if DEBUG
            GD.Print($"Effect: {effectId} not found.");
#endif
            return null;
        }

        public BaseStatusEffect Create(StatusEffectProto proto, params Variant[] args)
        {
            if (createFuncs.TryGetValue(proto.CreateFuncId, out var create))
            {
                return create(args);
            }
#if DEBUG
            GD.PrintErr($"Create Func: {proto.CreateFuncId} not found.");
#endif
            return null;
        }

        public Texture2D GetIcon(StringName id)
        {
            if (statusEffects.TryGetValue(id, out var proto))
            {
                return proto.Icon;
            }

#if DEBUG
            GD.PrintErr($"Status Effect: {id} not found");
#endif
            return null;
        }
        private static CreateFunc CtorOf<E>() where E : BaseStatusEffect
        {
            var type = typeof(E);
            var ctor = type.GetConstructors()[0];

            var parameters = ctor.GetParameters();

            return args => (E)ctor.Invoke(
                parameters
                    .Select(p => p.ParameterType)
                    .Zip(args)
                    .Select(p => p.Second.AsObj(p.First))
                    .ToArray()
            );
        }
    }
}